package cn.tedu.csmall.product.webapi.service;

import cn.tedu.csmall.commons.exception.ServiceException;
import cn.tedu.csmall.commons.response.ServiceCode;
import cn.tedu.csmall.pojo.dto.CategoryAddNewDTO;
import cn.tedu.csmall.pojo.entity.Category;
import cn.tedu.csmall.pojo.vo.CategoryListItemVO;
import cn.tedu.csmall.pojo.vo.CategoryStandardVO;
import cn.tedu.csmall.product.service.ICategoryService;
import cn.tedu.csmall.product.webapi.cache.repo.ICategoryCacheRepository;
import cn.tedu.csmall.product.webapi.mapper.CategoryMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Random;

import static cn.tedu.csmall.commons.response.ServiceCode.*;

@Service
@Slf4j
public class CategoryServiceImpl implements ICategoryService {

    @Autowired
    private CategoryMapper categoryMapper;

    /**
     * 重建缓存的间隔时间，以分钟为单位
     */
    public static final Integer REBUILD_INTERVAL_TIME_IN_MINUTE = 1;

    @Override
    public Long addNew(CategoryAddNewDTO categoryAddNewDTO) {
        // 日志
        log.debug("增加类别，参数：{}", categoryAddNewDTO);
        // 从参数中获取尝试增加的类别的名称
        String name = categoryAddNewDTO.getName();
        // 调用mapper对象的getByName()方法执行查询，并获取返回值
        CategoryStandardVO checkNameQueryResult = categoryMapper.getByName(name);
        // 判断返回值是否不为null
        if (checkNameQueryResult != null) {
            // 是：存在与名称匹配的数据，则名称已经被占用，抛出异常（暂时抛出RuntimeException）
            log.warn("增加类别失败，尝试增加的类别名称【{}】已经存在！", name);
            throw new ServiceException(CONFLICT, "增加类别失败，尝试增加的类别名称已经存在！");
        }

        // 从参数中获取父类类别的id
        Long parentId = categoryAddNewDTO.getParentId();
        // 默认类别深度为1
        Integer depth = 1;
        // 声明父级类别
        CategoryStandardVO parentCategory = null;
        // 判断父级类别的id是否为0
        if (parentId != 0) {
            // 当前尝试增加的类别不是一级分类，需要根据父类类别来决定当前类别的深度
            // 基于父级类别id,调用mapper对象的getById()方法执行查询，查询父级类别
            parentCategory = categoryMapper.getById(parentId);
            // 判断查询结果是否为null
            if (parentCategory == null) {
                // 是：父级类别不存在，抛出异常（暂时抛出RuntimeException）
                log.warn("增加类别失败，选定的父级类别不存在！");
                throw new ServiceException(NOT_FOUND, "增加类别失败，选定的父级类别不存在！");
            } else {
                // 否：父级类别存在，从查询结果中获取父类级别的depth，将其增加1，得到当前尝试增加的类别的深度
                depth = parentCategory.getDepth() + 1;
            }
        }

        // 创建Category类型的对象
        Category category = new Category();
        // 补全Category对象的属性值：name / parentId / keywords / sort / icon / enable / display：来自参数categoryAddNewDTO
        BeanUtils.copyProperties(categoryAddNewDTO, category);
        // 补全Category对象的属性值：depth：以上处理的结果
        category.setDepth(depth);
        // 补全Category对象的属性值：parent：默认为0
        category.setParent(0);
        // 补全Category对象的属性值：gmtCreate / gmtModified：当前时间（LocalDateTime.now()）
        LocalDateTime now = LocalDateTime.now();
        category.setGmtCreate(now);
        category.setGmtModified(now);
        // 调用mapper对象的insert()方法执行插入数据，并获取返回值
        log.debug("增加类别，即将插入类别数据：{}", category);
        int rows = categoryMapper.insert(category);
        // 判断返回的受影响行数是否不为1
        if (rows != 1) {
            // 是：抛出异常（暂时抛出RuntimeException）
            log.warn("增加类别失败，插入类别数据时出现未知错误！");
            throw new ServiceException(INTERNAL_SERVER_ERROR, "增加类别失败，服务器忙，请稍后再次尝试！");
        }

        // 判断，父级类别不为0，且父级类别中的parent值为0
        if (parentId != 0 && parentCategory.getParent() == 0) {
            // 基于父级别的id，调用mapper的updateParentById()，将父级类别的“parent：是否为父级类别”更新为1，并获取返回值
            log.debug("增加类别，将父级类别的“是否为父级类别”设置为：是");
            rows = categoryMapper.updateParentById(parentId, 1);
            // 判断返回的受影响行数是否不为1
            if (rows != 1) {
                // 是：抛出异常（暂时抛出RuntimeException）
                log.warn("增加类别失败，更新父级类别数据时出现未知错误！");
                throw new ServiceException(INTERNAL_SERVER_ERROR, "增加类别失败，服务器忙，请稍后再次尝试！");
            }
        }

        // 返回新增的类别的id，即调用insert()时的参数中的id属性值
        log.debug("增加类别，完成，将返回新增的类别的id：{}", category.getId());
        return category.getId();
    }

    @Override
    public CategoryStandardVO getById(Long id) {
        // 根据id从缓存中获取数据，并判断是否不为null
        CategoryStandardVO category = categoryCacheRepository.getCategoryById(id);
        if (category != null) {
            // 是：缓存中存在有效数据，则返回
            return category;
        }

        // 判断此Key是否存在
        boolean hasKey = categoryCacheRepository.hasKey(id);
        // 是：Key存在，却是null，抛出异常
        if (hasKey) {
            throw new ServiceException(NOT_FOUND, "获取类别详情失败，尝试访问的数据不存在！");
        }

        // 查数据库，判断是否存在此数据
        CategoryStandardVO categoryInDB = categoryMapper.getById(id);
        if (categoryInDB != null) {
            // 是：存在，则将此数据写入缓存，并返回
            categoryCacheRepository.setCategory(categoryInDB);
            return categoryInDB;
        } else {
            // 否：不存在，则向缓存中写入空值，并抛出异常
            categoryCacheRepository.setEmptyValue(id);
            throw new ServiceException(NOT_FOUND, "获取类别详情失败，尝试访问的数据不存在！");
        }
    }

    @Override
    public List<CategoryListItemVO> list() {
        // return categoryMapper.list();
        return categoryCacheRepository.list();
    }

    @Override
    public void initCache() {
        loadCache();
    }

    @Override
    public void rebuildCache() {
        // 读取“最近重建缓存”的时间
        Long recentRebuildTime = categoryCacheRepository.getRecentRebuildTime();
        if (recentRebuildTime == null) {
            recentRebuildTime = 0L;
        }
        // 与当前时间对比，判断时间差是否大于xx分钟
        long currentTimeMillis = System.currentTimeMillis();
        if (currentTimeMillis - recentRebuildTime > REBUILD_INTERVAL_TIME_IN_MINUTE * 60 * 1000) {
            // 是：两次重建缓存的间隔时间较长，允许重建缓存
            loadCache();
        } else {
            // 否：两次重建缓存的间隔时间太短，则抛出异常
            throw new ServiceException(NOT_ACCEPTABLE, "重建缓存失败，重建缓存必须间隔至少" + REBUILD_INTERVAL_TIME_IN_MINUTE + "分钟！");
        }
    }

    @Autowired
    private ICategoryCacheRepository categoryCacheRepository;

    /**
     * 加载类别数据的缓存
     */
    private void loadCache() {
        // 1. 从数据库中读取类别列表
        List<CategoryListItemVO> list = categoryMapper.list();
        List<CategoryStandardVO> listDetails = categoryMapper.listDetails();
        // 2. 清除缓存的类别数据
        categoryCacheRepository.clear();
        // 3. 将类别数据写入到缓存
        categoryCacheRepository.appendList(list);
        for (CategoryStandardVO category : listDetails) {
            categoryCacheRepository.setCategory(category);
        }
        // 4. 写入“最近重建缓存”的时间
        categoryCacheRepository.setRecentRebuildTime(System.currentTimeMillis());
    }

}
